//
// Created by moon on 2023/9/19.
//

#include "stm32Update.h"
#include <cstring>

#include "ArduinoAPI/Arduino.h"

#include "stm32f1_flash.h"

int Stm32Update::init(uint32_t app0_addr, uint32_t app1_addr) {
    flash_read(update_info_addr, (uint32_t *) &firmware_data, sizeof(FlashUpdateInfo_t));

    updateInfo = &firmware_data.info;
    /** no update history */
    if (updateInfo->flag != UPDATE_FLAG) {
        memset(&firmware_data, 0, sizeof(FlashUpdateInfo_t));

        updateInfo->flag     = UPDATE_FLAG;
        updateInfo->update   = 0;
        updateInfo->last_app = APP0;
        updateInfo->app_addr[0] = app0_addr;
        updateInfo->app_addr[1] = app1_addr;
        updateInfo->update_count = 0;
        file        = &firmware_data.file0;
        update_addr = app0_addr;

        flash_write_safe(update_info_addr, (uint32_t *) &firmware_data, sizeof(FlashUpdateInfo_t));
        return 0;
    }

//    file        = (updateInfo->last_app == APP0 ? &firmware_data.file1 : &firmware_data.file0);
//    update_addr = (updateInfo->last_app == APP0 ? app1_addr : app0_addr);
    file        = &firmware_data.file0;
    update_addr = app0_addr;

    if (updateInfo->update) {
        return 1;
    }

    return 0;
}


void Stm32Update::successful() {
//    updateInfo->last_app = (updateInfo->last_app == APP0 ? APP1 : APP0);
    updateInfo->update_count += 1;
    updateInfo->update = 0;

    flash_write_safe(update_info_addr, (uint32_t *) &firmware_data, sizeof(FlashUpdateInfo_t));
}

void Stm32Update::fail() {
    updateInfo->update = 0;
    flash_write_safe(update_info_addr, (uint32_t *) &firmware_data, sizeof(FlashUpdateInfo_t));
}


void Stm32Update::gotoApp() {
    typedef void (*jump)();
    jump jump_to_app;

    uint32_t app_addr = (updateInfo->last_app == APP0 ?
                         updateInfo->app_addr[0] : updateInfo->app_addr[1]);

    // 设置VTOR指向应用程序的向量表
    SCB->VTOR = app_addr;

    // 初始化应用程序堆栈指针
    __set_MSP(*(__IO uint32_t *) app_addr);

    // 获取应用程序的复位地址
    jump_to_app = (jump) (*(__IO uint32_t *) (app_addr + 4));

    // 跳转到应用程序
    jump_to_app();
}


/**
 * uart write
 * @param data data pointer
 * @param len transfer size
 * @return
 */
uint32_t Stm32Update::write(uint8_t *data, uint32_t len) {
    auto size = Serial.write(data, len);
    return size;
}

/**
 * uart read
 * @param data data pointer
 * @param len receive size
 * @return
 */
uint32_t Stm32Update::read(uint8_t *data, uint32_t len) {
    auto size = Serial.read(data, len);
    return size;
}


/**
 * \brief YModem file operation callback function.
 * \param type file operation type.
 * \param data write data or read data pointer.
 * \param len write data length.
 * \return successful = 0,fail = -1, or return data length.
 */
int Stm32Update::fileOperate(YModem::FileOperateType type, uint8_t *data, uint32_t len) {
    switch (type) {
    case YModem::FileOpenWriteOnly:
        /** erase flash space
         * Replace with your function
         * */
        // erase_flash_page(USER_APP_START_ADDR,
        //                  USER_APP_START_ADDR + USER_APP_MAX_SIZE);
        return OperateSuccess;

    case YModem::FileClose:
        read_addr  = 0;
        write_addr = 0;
        return OperateSuccess;

    case YModem::FileSetName:
        memset(file->name, 0, sizeof(file->name));
        strcpy(file->name, (const char *) data);
        return OperateSuccess;

    case YModem::FileSetSize:
        file->size = len;
        return OperateSuccess;

    case YModem::FileWrite:
        /** write flash space
         * Replace with your function
         * */
        // program_flash(USER_APP_START_ADDR + write_addr,
        //                   (uint32_t *) data, len);
        write_addr += len;
        return len;

    case YModem::FileRead:
        /** read flash space
         * Replace with your function
         * */
        // read_flash(USER_APP_START_ADDR + read_addr,
        //                (uint32_t *) data, len);
        read_addr += len;
        return len;

    default:
        break;
    }

    return OperateFail;
}

/**
 * YModem file open callback function.
 * @param type file operate type
 * @return successful = 0, fail = -1
 */
int Stm32Update::fileOpen(YModem::FileOperateType type) {
    // erase_flash_page(USER_APP_START_ADDR,
    //                  USER_APP_START_ADDR + USER_APP_MAX_SIZE);
//    flash_erase(update_addr, update_max_size);

    return YModem::OperateSuccess;
}


int Stm32Update::fileClose() {
    read_addr  = 0;
    write_addr = 0;

    return YModem::OperateSuccess;
}

void Stm32Update::fileSetProperty(YModem::FileProperty_t *property) {
    strcpy(file->name, property->name);
    file->size = property->size;
}


uint32_t Stm32Update::fileWrite(uint8_t *data, uint32_t size) {
    // program_flash(USER_APP_START_ADDR + write_addr,
    //                   (uint32_t *) data, len);

    flash_write_safe(update_addr + write_addr, (uint32_t *) data, size);

    write_addr += size;
    return size;

}

uint32_t Stm32Update::fileRead(uint8_t *data, uint32_t size) {
    // read_flash(USER_APP_START_ADDR + read_addr,
    //                (uint32_t *) data, len);
    flash_read(update_addr + read_addr, (uint32_t *) data, size);

    read_addr += size;
    return size;
}





